package org.iot.iotserver.http.servlet;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import org.iot.iotserver.http.AntPathMatcher;
import org.iot.iotserver.netty.config.NettyConstant;
import org.iot.iotserver.netty.entity.ADevice;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@WebServlet(name = "YIOTServlet", urlPatterns = { "/api/*" })
public class YIOTServlet extends YAbstractServlet {

    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String path = req.getRequestURI();

        if(pathMatcher.match("/api/devices/**", path)) {
            queryDevices(req, resp);
        } else if(pathMatcher.match("/api/control/**", path)) {
            ledControl(req, resp);
        } else {
            responseJsonData(resp, new JSONObject().set("code", "404").toString());
        }
    }

    /**
     * 查询设备信息
     * @param req
     * @param resp
     * @throws IOException
     */
    private void queryDevices(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String path = req.getRequestURI();

        // 查所有
        if("/api/devices".equals(path)) {
            List<ADevice> devices = CollectionUtil.newArrayList();
            for (String legalDeviceName : NettyConstant.LEGAL_DEVICE_NAMES) {
                devices.add( NettyConstant.DEVICE_CACHE.get(legalDeviceName));
            }
            responseJsonData(resp, JSONUtil.toJsonStr(devices));
            return;
        }

        // 查特定设备
        Map<String, String> params = pathMatcher.extractUriTemplateVariables("/api/devices/{id}", path);
        String deviceId = params.getOrDefault("id", "null");

        if(!NettyConstant.DEVICE_CACHE.containsKey(deviceId)) {
            responseJsonData(resp, new JSONObject().set("code", "400").toString());
        }

        responseJsonData(resp, JSONUtil.toJsonStr(NettyConstant.DEVICE_CACHE.get(deviceId)));
    }

    /**
     * 远程控制 led
     * @param req
     * @param resp
     * @throws IOException
     */
    private void ledControl(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String deviceId = req.getParameter("deviceId");

        Optional<Channel> channelOpt = NettyConstant.CHANNELS.stream().filter(channel -> {
            if (channel.hasAttr(NettyConstant.DEVICE_KEY)) {
                return deviceId.equalsIgnoreCase(String.valueOf(channel.attr(NettyConstant.DEVICE_KEY).get()));
            }
            return false;
        }).findFirst();

        String r404 = new JSONObject().set("code", "404").toString();
        String r200 = new JSONObject().set("code", "200").toString();

        if(channelOpt.isPresent()) {
            String command = req.getParameter("command");
            Channel targetChannel = channelOpt.get();
            targetChannel.writeAndFlush("led " + command + "\r\n");
            int count = 0;
            int retryCount = 0;
            boolean isOk = false;

            do {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                ADevice aDevice = NettyConstant.DEVICE_CACHE.get(deviceId);
                String led = aDevice.getData().get("led");

                if (command.equals(led)) {
                    isOk = true;
                    break;
                }
                count++;

                // 3 秒之类状态未变化, 重试 3 次(最多重试 3 次)
                if(retryCount < 3 && count > 2) {
                    targetChannel.writeAndFlush("led " + command + "\r\n");
                    count = 0;
                    retryCount++;
                }

            } while (count < 5);

            responseJsonData(resp, isOk ? r200 : r404);
            return;
        }

        responseJsonData(resp, r404);
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}
